unit main;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, PrintersDlgs, SynHighlighterPas, Forms, Controls,
  Graphics, Dialogs, Menus, ActnList, ComCtrls, StdCtrls, ExtCtrls, Buttons,
  magic, Printers, Clipbrd, Spin, math, types,   ImageMagick,
  dlgabout,ShellApi,LResources, undo;

const
  CURSOR_BRUSH=1;
  CURSOR_RECTANGLE=2;
  CURSOR_MOVE=3;
  CURSOR_COLORPICKER=4;
  CURSOR_BORDER=5;

type



  { TMainForm }

  TMainForm = class(TForm)
    acExit: TAction;
    acOpen: TAction;
    acAdd: TAction;
    acSaveAs: TAction;
    acPrint: TAction;
    acPageUp: TAction;
    acPageDown: TAction;
    acCopy: TAction;
    acDrawPolyline: TAction;
    acSetColor: TAction;
    acAbout: TAction;
    acHelpOnline: TAction;
    acSetMultiplayMode: TAction;
    acFileNew: TAction;
    acDrawRectangle: TAction;
    acUndo: TAction;
    ActionList1: TActionList;
    ApplicationProperties1: TApplicationProperties;
    cbScale: TComboBox;
    btnSetColor: TColorButton;
    edCurPage: TEdit;
    imgView: TImage;
    ImageList1: TImageList;
    Label1: TLabel;
    lbPageCount: TLabel;
    MainMenu1: TMainMenu;
    MenuItem1: TMenuItem;
    MenuItem10: TMenuItem;
    MenuItem11: TMenuItem;
    MenuItem12: TMenuItem;
    miUndo: TMenuItem;
    miRectangle: TMenuItem;
    miNew: TMenuItem;
    miOnlineHelp: TMenuItem;
    miAbout: TMenuItem;
    MenuItem2: TMenuItem;
    MenuItem3: TMenuItem;
    MenuItem5: TMenuItem;
    MenuItem6: TMenuItem;
    MenuItem7: TMenuItem;
    MenuItem8: TMenuItem;
    MenuItem9: TMenuItem;
    miCopy: TMenuItem;
    miHelp: TMenuItem;
    miEdit: TMenuItem;
    miPrint: TMenuItem;
    MenuItem4: TMenuItem;
    miExit: TMenuItem;
    miSaveAs: TMenuItem;
    miAdd: TMenuItem;
    miFile: TMenuItem;
    miFileOpen: TMenuItem;
    dlgOpen: TOpenDialog;
    dlgSave: TSaveDialog;
    pnPenSize: TPanel;
    pnScale: TPanel;
    pnPages: TPanel;
    dlgPrint: TPrintDialog;
    ScrollBox1: TScrollBox;
    spPenSize: TSpinEdit;
    ToolBar1: TToolBar;
    ToolButton1: TToolButton;
    ToolButton10: TToolButton;
    ToolButton11: TToolButton;
    ToolButton12: TToolButton;
    ToolButton13: TToolButton;
    btnRectangle: TToolButton;
    ToolButton14: TToolButton;
    tbbUndo: TToolButton;
    ToolButton2: TToolButton;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    ToolButton5: TToolButton;
    ToolButton6: TToolButton;
    ToolButton7: TToolButton;
    ToolButton8: TToolButton;
    btnPaint: TToolButton;
    tbProcesAllPages: TToolButton;
    ToolButton9: TToolButton;
    procedure acAboutExecute(Sender: TObject);
    procedure acAddExecute(Sender: TObject);
    procedure acCopyExecute(Sender: TObject);
    procedure acCopyUpdate(Sender: TObject);
    procedure acDrawPolylineExecute(Sender: TObject);
    procedure acDrawPolylineUpdate(Sender: TObject);
    procedure acDrawRectangleExecute(Sender: TObject);
    procedure acDrawRectangleUpdate(Sender: TObject);
    procedure acExitExecute(Sender: TObject);
    procedure acFileNewExecute(Sender: TObject);
    procedure acHelpOnlineExecute(Sender: TObject);
    procedure acOpenExecute(Sender: TObject);
    procedure acPageDownExecute(Sender: TObject);
    procedure acPageDownUpdate(Sender: TObject);
    procedure acPageUpExecute(Sender: TObject);
    procedure acPageUpUpdate(Sender: TObject);
    procedure acPrintExecute(Sender: TObject);
    procedure acPrintUpdate(Sender: TObject);
    procedure acSaveAsExecute(Sender: TObject);
    procedure acSaveAsUpdate(Sender: TObject);
    procedure acSetColorExecute(Sender: TObject);
    procedure acSetMultiplayModeExecute(Sender: TObject);
    procedure acSetMultiplayModeUpdate(Sender: TObject);
    procedure cbScaleChange(Sender: TObject);
    procedure edCurPageKeyPress(Sender: TObject; var Key: char);
    procedure FormCreate(Sender: TObject);

    procedure FormDestroy(Sender: TObject);
    procedure FormDropFiles(Sender: TObject; const FileNames: array of String);
    procedure FormShow(Sender: TObject);
    procedure imgViewMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure imgViewMouseLeave(Sender: TObject);
    procedure imgViewMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure imgViewMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure imgViewMouseWheel(Sender: TObject; Shift: TShiftState;
      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
    procedure ScrollBox1Resize(Sender: TObject);


  private
    { private declarations }
    ffilename:string;
    fmagic:TMyMagic;
    fbmp:TBitmap;
    fcurscale:double;

    //last_x,last_y:integer;
    draw_mode:TMyOperationType;

    points:array[1..3000] of PointInfo ;
    numpoints:integer;

    function GetMagic:TMyMagic;
    procedure SetFileName(AValue: string);
    procedure UpdateCurrentView;
    procedure UpdateCurrentViewPosition;
    procedure InitPageNavigator;
    procedure AddFile(fname:string);
    procedure SetModeCursor;
    procedure SetPanMode;
    procedure SetDrawParams;
    function CalculateRect(np: integer):TRect;
    function CanScroll:boolean;
  public
    { public declarations }
    property magic: TMyMagic read GetMagic;
    property filename:string read ffilename write SetFileName;

  end;

var
  MainForm: TMainForm;

implementation

{$R *.lfm}


{ TMainForm }

procedure TMainForm.acExitExecute(Sender: TObject);
begin
  Close();
end;

procedure TMainForm.acFileNewExecute(Sender: TObject);
begin
  magic.Clear;
  filename:= '';
  UpdateCurrentView;
  InitPageNavigator;
end;

procedure TMainForm.acHelpOnlineExecute(Sender: TObject);
begin
  ShellExecute(0,'open',PChar('http://pdfcorrector.5co.org/home/usermanual'),nil,nil,1);
end;

procedure TMainForm.acAddExecute(Sender: TObject);
var c:integer;
begin
  if dlgOpen.Execute then
  begin
     AddFile(dlgOpen.FileName);
  end;
  SetPanMode;
end;

procedure TMainForm.acAboutExecute(Sender: TObject);
begin
  frmAbout.ShowModal;
end;

procedure TMainForm.acCopyExecute(Sender: TObject);
var b:TBitmap;
    r:TRect;
begin
  b:=TBitmap.Create;
  try
    b.Width:=imgView.Width;
    b.Height:=imgView.Height;
    r:=imgView.ClientRect;
    b.Canvas.StretchDraw(r, imgView.Picture.Bitmap);
    Clipboard.Assign(b);
  finally
    b.Free;
  end;
end;

procedure TMainForm.acCopyUpdate(Sender: TObject);
begin
   acCopy.Enabled:= (magic.ImgCount>0);
end;

procedure TMainForm.acDrawPolylineExecute(Sender: TObject);
begin
  with acDrawPolyline do
  begin
    Checked:= not Checked;
    if Checked then
    begin
       draw_mode:=mmPolyLine;
       acDrawRectangle.Checked:=false;
    end
    else
       draw_mode:=mmPan;
  end;
  SetModeCursor;
end;

procedure TMainForm.acDrawPolylineUpdate(Sender: TObject);
begin
  acDrawPolyline.Enabled:=magic.ImgCount>0;
  acSetColor.Enabled:=acDrawPolyline.Enabled ;
  btnSetColor.Enabled:=acDrawPolyline.Enabled  ;
  spPenSize.Enabled:=acDrawPolyline.Enabled ;
end;

procedure TMainForm.acDrawRectangleExecute(Sender: TObject);
begin
  with acDrawRectangle do
  begin
    Checked:= not Checked;
    if Checked then
    begin
       draw_mode:=mmRectangle ;
       acDrawPolyLine.Checked:=false;
    end
    else
       draw_mode:=mmPan;
  end;
  SetModeCursor;
end;

procedure TMainForm.acDrawRectangleUpdate(Sender: TObject);
begin
  acDrawRectangle.Enabled:=magic.ImgCount>0;
end;

procedure TMainForm.acOpenExecute(Sender: TObject);
begin
  if dlgOpen.Execute then
  begin
     magic.Clear;
     AddFile(dlgOpen.FileName);
     filename:= dlgOpen.FileName;
  end;
    SetPanMode;
end;

procedure TMainForm.AddFile(fname:string);
var i:integer;
    c1,c2,c3,c4:TCursor;
begin
  c1:=ScrollBox1.Cursor;
  c2:=imgView.Cursor;
  c3:=ToolBar1.Cursor;
  c4:=Self.Cursor;

  ScrollBox1.Cursor:=crHourGlass;
  imgView.Cursor:=crHourGlass;
  ToolBar1.Cursor:=crHourGlass;
  Self.Cursor :=crHourGlass;

  i:=magic.ImgCount;
  magic.LoadFromFile(fname);
  if i < magic.ImgCount then
    magic.ImgIndex:=i;
  UpdateCurrentView;
  InitPageNavigator;

  ScrollBox1.Cursor:=c1;
  imgView.Cursor:=c2;
  ToolBar1.Cursor:=c3;
  Self.Cursor:=c4;
end;

procedure TMainForm.SetModeCursor;
begin
  with imgView do
  begin
    case draw_mode of
      mmRectangle:Cursor:= CURSOR_RECTANGLE;
      mmPolyLine:Cursor:= CURSOR_BRUSH;
      mmPan:
        begin
         if CanScroll then
            Cursor:=CURSOR_MOVE
         else
            Cursor:=crDefault;
        end
    else
      Cursor:= crDefault;
    end;
  end;
end;

procedure TMainForm.SetPanMode;
begin
  acDrawPolyline.Checked:=false;
  acDrawRectangle.Checked:=false;
  draw_mode:=mmPan;
  SetModeCursor;
end;

procedure TMainForm.SetDrawParams;
begin
  with fbmp.Canvas do
  begin
   Pen.Color:=btnSetColor.ButtonColor;
   Brush.Color:=btnSetColor.ButtonColor;
   case draw_mode of
      mmRectangle:
        begin
           Pen.Width:= 1;
           Pen.Color:=clBlack;
        end;
      mmPolyLine:
        begin
         Pen.Width:=spPenSize.Value;
        end;
    end;
  end;
end;

function TMainForm.CalculateRect(np: integer):TRect;
var r :TRect;
begin
  r.Left:=min(round(points[1].x), round(points[np].x));
  r.Right:=max(round(points[1].x), round(points[np].x));
  r.Top:=min(round(points[1].y), round(points[np].y));
  r.Bottom:=max(round(points[1].y), round(points[np].y));
  Result:= r;
end;

function TMainForm.CanScroll: boolean;
begin
    Result:=(imgView.Width>ScrollBox1.Width) or (imgView.Height> ScrollBox1.Height);
end;

procedure TMainForm.acPageDownExecute(Sender: TObject);
begin
  magic.ImgIndex:=magic.ImgIndex+1;
  UpdateCurrentView;
  InitPageNavigator;
end;

procedure TMainForm.acPageDownUpdate(Sender: TObject);
begin
 acPageDown.Enabled:=magic.ImgIndex < (magic.ImgCount-1);
end;

procedure TMainForm.acPageUpExecute(Sender: TObject);
begin
   magic.ImgIndex:=magic.ImgIndex-1;
   UpdateCurrentView;
   InitPageNavigator;
end;

procedure TMainForm.acPageUpUpdate(Sender: TObject);
begin
  acPageUp.Enabled:=(magic.ImgIndex > 0);

  {разрешени ако има картинки}
  pnPages.Enabled:=magic.ImgCount>0;
  pnScale.Enabled:=pnPages.Enabled;
end;

procedure TMainForm.acPrintExecute(Sender: TObject);
var pgmin,pgmax,ci,i,j,cp,pgs:integer;
    b:TBitmap;
    k:double;
    cr:TRect;
begin
  SetPanMode;
  with dlgPrint do
  begin
   Title:='Отпечатване на документ '+ExtractFileName(filename);
   MinPage:=1;
   MaxPage:=magic.ImgCount;
   FromPage:= magic.ImgIndex+1;
   ToPage:= FromPage;
   if Execute then
   begin

     case PrintRange of
       prAllPages:
         begin
           pgmin:= 0;
           pgmax:= magic.ImgCount-1;
         end;
       prPageNums:
         begin
           pgmin:=FromPage -1;
           pgmax:=ToPage -1
         end
       else
        pgmin:= magic.ImgIndex;
        pgmax:=magic.ImgIndex;
      end;

      ci:=magic.ImgIndex;
      b:=TBitmap.Create;
      Printer.BeginDoc;
      Printer.Title:=ExtractFileName(filename);
      pgs:=(pgmax-pgmin+1)*Copies;
      cp:=0;
      for i:=pgmin to pgmax do
      begin
        magic.ImgIndex:=i;
        magic.GetCurrentImg(b);
        {
        k:=min( Printer.PageWidth/b.Width, Printer.PageHeight/b.Height)*0.8;
        cr.Left:=trunc(0.1*Printer.PageWidth);
        cr.Top:=trunc(0.1*Printer.PageHeight);
        }
        k:=min( Printer.PageWidth/b.Width, Printer.PageHeight/b.Height);
        cr.Left:=0 ;
        cr.Top:= 0;

        cr.Right:=trunc(b.Width*k)-1;
        cr.Bottom:=trunc(b.Height*k)-1;
        for j:= 1 to Copies do
        begin
          Printer.Canvas.StretchDraw(cr,b);
          Inc(cp);
          if cp<pgs then
            Printer.NewPage;
        end;
      end;
      Printer.EndDoc;
      b.Free;
      magic.ImgIndex:=ci;

   end;
  end;
end;

procedure TMainForm.acPrintUpdate(Sender: TObject);
begin
  acPrint.Enabled:=(magic.ImgCount>0);
end;

procedure TMainForm.acSaveAsExecute(Sender: TObject);
var buf:string;
begin
  if filename <> '' then
  begin
    buf:=filename;
    insert('_cor',buf,pos(ExtractFileExt(filename),buf));
    dlgSave.FileName:=buf;
  end;

  if dlgSave.Execute then
  begin
     magic.SaveToFile(dlgSave.FileName);
     filename:= dlgSave.FileName;
  end;
  SetPanMode;
end;

procedure TMainForm.acSaveAsUpdate(Sender: TObject);
begin
   acSaveAs.Enabled:=(magic.ImgCount>0);
end;

procedure TMainForm.acSetColorExecute(Sender: TObject);
begin
  btnSetColor.Click;
end;

procedure TMainForm.acSetMultiplayModeExecute(Sender: TObject);
begin
  acSetMultiplayMode.Checked:=not acSetMultiplayMode.Checked;
end;

procedure TMainForm.acSetMultiplayModeUpdate(Sender: TObject);
begin
  acSetMultiplayMode.Enabled:=magic.ImgCount>1;
end;

procedure TMainForm.cbScaleChange(Sender: TObject);
begin
  fcurscale:=(cbScale.ItemIndex+1)*0.25;
  UpdateCurrentViewPosition;
end;

procedure TMainForm.edCurPageKeyPress(Sender: TObject; var Key: char);
var i:integer;
begin
   if Ord(Key) = $0D then
   begin
     i:=StrToIntDef(edCurPage.Text,-1);
     if (i < 1) or (i > magic.ImgCount) then
       InitPageNavigator
      else if i <> magic.ImgIndex+1 then
      begin
        magic.ImgIndex:=i-1;
        UpdateCurrentView;
      end;
   end;
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  InitImageMagic;
  fmagic:=nil;
  fcurscale:=1.0;
  draw_mode:= mmPan;
  //fbmp:=TBitmap.Create -използва битмапа от imgView;
  fbmp:=imgView.Picture.Bitmap;
  Caption:= ApplicationProperties1.Title;

  Screen.Cursors[CURSOR_BRUSH] := LoadCursorFromLazarusResource('cbrush');
  Screen.Cursors[CURSOR_RECTANGLE] := LoadCursorFromLazarusResource('crectangle');
  Screen.Cursors[CURSOR_MOVE] := LoadCursorFromLazarusResource('cmove');
  Screen.Cursors[CURSOR_COLORPICKER] := LoadCursorFromLazarusResource('ccolorpicker');
  Screen.Cursors[CURSOR_BORDER] := LoadCursorFromLazarusResource('cborder');

end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  // fbmp.free-използва битмапа от imgView;
   if Assigned(fmagic) then
    fmagic.Free;
  FreeImageMagic;
end;

procedure TMainForm.FormDropFiles(Sender: TObject;
  const FileNames: array of String);
var i:integer;
    buf:string;
begin

  for i := Low(FileNames) to High(FileNames) do
  begin
    buf:=FileNames[i];
    if UpperCase(ExtractFileExt(buf))='.PDF' then
    begin
      AddFile(buf);
    end;
  end;
end;

procedure TMainForm.FormShow(Sender: TObject);
var buf:string;
begin
  if Application.ParamCount >= 1 then
  begin
    buf:=ParamStr(1);
    if UpperCase(ExtractFileExt(buf))='.PDF' then
    begin
      AddFile(buf);
      filename:=buf;
    end;
  end;
end;

procedure TMainForm.imgViewMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  SetDrawParams;
  SetModeCursor;

  numpoints:=1;
  points[numpoints].x:=X/fcurscale;
  points[numpoints].y:=Y/fcurscale;
end;

procedure TMainForm.imgViewMouseLeave(Sender: TObject);
begin
  numpoints:=0;
end;

procedure TMainForm.imgViewMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var r:TRect;
begin
  if ssLeft in Shift then
  begin
    Inc(numpoints);
    points[numpoints].x:=X/fcurscale;
    points[numpoints].y:=Y/fcurscale;

    if draw_mode = mmPan then
    begin
       {нищо засега}
    end
    else if (draw_mode = mmPolyLine) and (numpoints>2) then
    begin

       fbmp.Canvas.Line(
             round(points[numpoints-1].x),round(points[numpoints-1].y),
             round(points[numpoints].x),round(points[numpoints].y)
             );

    end
    else if draw_mode = mmRectangle then
    begin
       if numpoints>2 then
       begin
          r:=CalculateRect(numpoints-1);
          fbmp.Canvas.DrawFocusRect(r);
       end;
       r:=CalculateRect(numpoints);
       fbmp.Canvas.DrawFocusRect(r);
    end;
  end;
end;

procedure TMainForm.imgViewMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var ci,ei,i,dx,dy:integer;
    r:TRect;
begin
  if numpoints > 1 then
    if (draw_mode = mmPan) and (numpoints>2) then
    begin
      dx:=round((points[numpoints].x - points[1].x)*fcurscale);
      dy:=round((points[numpoints].y - points[1].y)*fcurscale);
      if (dx<>0) or (dy<>0) then
      begin
          with ScrollBox1 do
          begin
            { ScrollBy(-dx,-dy);   }
            Enabled:=false;
            HorzScrollBar.Position:=ScrollBox1.HorzScrollBar.Position-dx;
            VertScrollBar.Position:=ScrollBox1.VertScrollBar.Position-dy;
            Enabled:=true;

          end
      end;
    end
    else
    begin
        ci:= magic.ImgIndex;
        if acSetMultiplayMode.Checked then
         ei:=magic.ImgCount-1
        else
         ei:=ci;
        r:=CalculateRect(numpoints);

        for i:=ci to ei do
        begin
          magic.ImgIndex:=i;
          if draw_mode = mmPolyLine then
              magic.PolyLine(fbmp.Canvas.Pen.Color,fbmp.Canvas.Pen.Width,numpoints,@points[1])
          else if draw_mode = mmRectangle then
          begin
            {tuk se chertae zapulnen pravougalnik}
            if i = ci then
            begin
              fbmp.Canvas.FillRect(r);
            end;
            magic.Rectangle(fbmp.Canvas.Brush.Color,r.Left,r.Top,r.Right,r.Bottom);
          end;
        end;
        if magic.ImgIndex<>ci then
         magic.ImgIndex:=ci;
    end;

  numpoints:=0;
  SetModeCursor;
end;

procedure TMainForm.imgViewMouseWheel(Sender: TObject; Shift: TShiftState;
  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
  if (WheelDelta > 0) and (cbScale.ItemIndex < cbScale.Items.Count-1) then
    cbScale.ItemIndex:=cbScale.ItemIndex+1
  else if (WheelDelta < 0) and (cbScale.ItemIndex > 0) then
    cbScale.ItemIndex:=cbScale.ItemIndex-1;
  cbScaleChange(cbScale);
end;

procedure TMainForm.ScrollBox1Resize(Sender: TObject);
begin
  UpdateCurrentViewPosition;
  SetModeCursor;
end;


function TMainForm.GetMagic: TMyMagic;
begin
  if not Assigned(fmagic) then
     fmagic:= TMyMagic.Create;
  Result:= fmagic;
end;

procedure TMainForm.SetFileName(AValue: string);
begin
  if ffilename=AValue then Exit;
  ffilename:=AValue;
  if ffilename='' then
    Self.Caption:=ApplicationProperties1.Title
  else
    Self.Caption:=ExtractFileName(AValue) + ' - '+ApplicationProperties1.Title;
end;

procedure TMainForm.UpdateCurrentView;
begin
  magic.GetCurrentImg(fbmp);
  UpdateCurrentViewPosition;
  //imgView.Picture.Bitmap:=fbmp;
end;

procedure TMainForm.UpdateCurrentViewPosition;
var x,y,dx,dy:integer;
begin
  x:=0;
  y:=0;

  dx:= ScrollBox1.Width-trunc(fbmp.Width*fcurscale);
  if dx >0 then
   x:= dx div 2;
  dy:= ScrollBox1.Height-trunc(fbmp.Height*fcurscale);
  if dy > 0 then
   y:=dy div 2;

  imgView.SetBounds(x,y,trunc(fbmp.Width*fcurscale),trunc(fbmp.Height*fcurscale) ) ;
end;

procedure TMainForm.InitPageNavigator;
begin
  lbPageCount.Caption:=inttostr(magic.ImgCount);
  edCurPage.Text:= inttostr(magic.ImgIndex+1);
end;

initialization
{$I pdfcorrector.lrs}
end.

